﻿/* 
    Copyright (c) 2010 Microsoft Corporation.  All rights reserved.
    Use of this sample source code is subject to the terms of the Microsoft license 
    agreement under which you licensed this sample source code and is provided AS-IS.
    If you did not accept the terms of the license agreement, you are not authorized 
    to use this sample source code.  For the terms of the license, please see the 
    license agreement between you and Microsoft.
    
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace PerFrameCallback
{
    /// <summary>
    /// This example shows a square that is animated by a Storyboard defined in XAML. This animation runs 
    /// on the compositor thread and most of the work is done on the GPU. Also, this square is automatically
    /// cached. This example also allows you to add a number of rectangles that are animated on the UI thread
    /// in the CompositionTarget.Rendering event handler. When caching is enabled for the rectangles, the fill rate
    /// goes down. In this case the square animation runs smoothly because the GPU is limited
    /// by the fill rate, but is not affected by the number of rectangles animated on the UI thread. When caching is 
    /// disabled, the rectangles are redrawn with every update, increasing the fill rate and causing the square 
    /// animation to be choppy.
    /// </summary>
    public partial class MainPage : PhoneApplicationPage
    {
        private List<Rectangle> _rectList;
        private List<bool> _rectDirection;
        private List<Color> _rectColors;
        private Random _rand;
        private bool _isCached = true;
        private bool _isRedraw = false;
        private DateTime _lastTick;
        
        /// <summary>
        /// The page's constructor.
        /// </summary>
        public MainPage()
        {
            InitializeComponent();

            // Enable the frame rate counter.
            Application.Current.Host.Settings.EnableFrameRateCounter = true;

            // Initialize lists for the animated rectangles.
            _rectList = new List<Rectangle>();
            _rectDirection = new List<bool>();
            _rectColors = new List<Color>() { Colors.Blue, Colors.Red, Colors.Green, Colors.Cyan, Colors.Magenta, Colors.Orange, Colors.Purple, Colors.Yellow, Colors.Brown, Colors.Gray };
            _rand = new Random();

            // Add an event handler for the CompositionTarget.Rendering event in order to animate the rectangles.
            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

            _lastTick = DateTime.Now;
        }
        /// <summary>
        /// Event handler for the CompositionTarget.Rendering event.  In this method, the list of rectangles added by
        /// the user are animated back and forth across the screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            // Use the elapsed time since the last frame to adjust the distance each rectangle moves for each frame
            DateTime now = DateTime.Now;
            TimeSpan elapsed = now - _lastTick;
            _lastTick = now;
            double elapsedModifier = (elapsed.Milliseconds / 16.0);

            // Loop through the list of rectangles
            for (int i = 0; i < _rectList.Count; i++)
            {
                // Get the current position of the rectangle.
                double x = (double) _rectList[i].GetValue(Canvas.LeftProperty);

                // Move the rectangle, depending on the value of _rectDirection. If the rectangle moves off the screen,
                // reverse the direction.
                if (_rectDirection[i] == false)
                {
                    x = x - 5 * elapsedModifier;
                    if (x <= 0)
                    {
                        _rectDirection[i] = true;
                    }

                    // Set the new location of the rectangle.
                    _rectList[i].SetValue(Canvas.LeftProperty, x);
                }
                else
                {
                    x = x + 5 * elapsedModifier;
                    if (x >= 480)
                    {
                        _rectDirection[i] = false;
                    }

                    _rectList[i].SetValue(Canvas.LeftProperty, x);
                }
            }

        }


        /// <summary>
        /// The Delete button click handler. This method removes the last rectangle from the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void deleteBtn_Click(object sender, RoutedEventArgs e)
        {
            if (_rectList.Count >= 1)
            {

                this.LayoutRoot.Children.Remove(_rectList[_rectList.Count - 1]);
                _rectList.Remove(_rectList[_rectList.Count - 1]);
                _rectDirection.Remove(_rectDirection[_rectDirection.Count - 1]);

                UpdateLayout();
                counter.Text = _rectList.Count.ToString();
            }

        }
        /// <summary>
        /// The Add button click handler. This method adds a new rectangle to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void addBtn_Click(object sender, RoutedEventArgs e)
        {
            Rectangle newRect = new Rectangle();
            newRect.Height = 800;
            newRect.Width = 60;

            newRect.Fill = new SolidColorBrush(_rectColors[_rand.Next(10)]);

            if (_isCached)
            {
                newRect.CacheMode = new BitmapCache();
            }
            
            _rectList.Add(newRect);

            _rectDirection.Add(true);
            counter.Text = _rectList.Count.ToString();
            this.LayoutRoot.Children.Add(newRect);

        }

        /// <summary>
        /// The Show button click handler. This method shows the buttons at the bottom of the screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showBtn_Click(object sender, RoutedEventArgs e)
        {
            showBtn.Visibility = Visibility.Collapsed;
            hideBtn.Visibility = Visibility.Visible;
            addBtn.Visibility = Visibility.Visible;
            deleteBtn.Visibility = Visibility.Visible;
        }
        /// <summary>
        /// The Hide button click handler. This method hides the buttons at the bottom of the screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void hideBtn_Click(object sender, RoutedEventArgs e)
        {
            showBtn.Visibility = Visibility.Visible;
            hideBtn.Visibility = Visibility.Collapsed;
            addBtn.Visibility = Visibility.Collapsed;
            deleteBtn.Visibility = Visibility.Collapsed;
        }
        /// <summary>
        /// The Cache button click handler. This method toggles all of the animated rectangles between using
        /// BitmapCache and not being cached. Note that when caching is disabled, the animation of the square
        /// becomes choppy. This is because the rectangles are being redrawn completely with each update which
        /// increases the fill rate. When the rectangles are cached, the number of pixels drawn is lower.
        /// The square animation running on the compositor thread is limited by the total fill rate, but is
        /// not affected by the drawing of the rectangles on the UI thread.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cacheBtn_Click(object sender, RoutedEventArgs e)
        {
            foreach (Rectangle rect in _rectList)
            {
                if (_isCached)
                {
                    rect.CacheMode = null;
                    cacheDisplay.Text = "Cache Off";
                }
                else
                {
                    rect.CacheMode = new BitmapCache();
                    cacheDisplay.Text = "Cache On";
                }
            }
            _isCached = !_isCached;
        }

        /// <summary>
        /// Redraw button click handler. This method toggles the EnableDrawRegions property. When this
        /// is enabled, if you observe a lot of flashing on the screen, it means that a lot of pixels 
        /// are being drawn that are underneath other pixels, possibly indicating wasteful drawing that
        /// is increases the fill rate.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void redrawBtn_Click(object sender, RoutedEventArgs e)
        {
            _isRedraw = !_isRedraw;
            Application.Current.Host.Settings.EnableRedrawRegions = _isRedraw;
        }
    }
}
